home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-03-27 | 47.7 KB | 1,018 lines | [TEXT/ROSA] |
- Common Lisp the Language, 2nd Edition
- -------------------------------------------------------------------------------
-
- 27. Pretty Printing
-
- by Richard C. Waters
-
- [change_begin]
- PREFACE: X3J13 voted in January 1989 (PRETTY-PRINT-INTERFACE) to adopt a
- facility for user-controlled pretty printing as a part of the forthcoming draft
- Common Lisp standard. This facility is the culmination of thirteen years of
- design, testing, revision, and use of this approach.
-
- This chapter presents the bulk of the Common Lisp pretty printing
- specification, written by Richard C. Waters. I have edited it only very lightly
- to conform to the overall style of this book.
-
- - Guy L. Steele Jr.
-
- [change_end]
- -------------------------------------------------------------------------------
-
- * Introduction
- * Pretty Printing Control Variables
- * Dynamic Control of the Arrangement of Output
- * Format Directive Interface
- * Compiling Format Control Strings
- * Pretty Printing Dispatch Tables
-
- -------------------------------------------------------------------------------
-
- 27.1. Introduction
-
- [change_begin]
- Pretty printing has traditionally been a black box process, displaying program
- code using a set of fixed layout rules. Its utility can be greatly enhanced by
- opening it up to user control. The facilities described in this chapter provide
- general and powerful means for specifying pretty-printing behavior.
-
- By providing direct access to the mechanisms within the pretty printer that
- make dynamic decisions about layout, the macros and functions
- pprint-logical-block, pprint-newline, and pprint-indent make it possible to
- specify pretty printing layout rules as a part of any function that produces
- output. They also make it very easy for the function to support detection of
- circularity and sharing and abbreviation based on length and nesting depth.
- Using the function set-pprint-dispatch, one can associate a user-defined pretty
- printing function with any type of object. A small set of new format directives
- allows concise implementation of user-defined pretty-printing functions.
- Together, these facilities enable users to redefine the way code is displayed
- and allow the full power of pretty printing to be applied to complex
- combinations of data structures.
-
- -------------------------------------------------------------------------------
- Implementation note: This chapter describes the interface of the XP pretty
- printer. XP is described fully in [54], which also explains how to obtain a
- portable implementation. XP uses a highly efficient linear-time algorithm. When
- properly integrated into a Common Lisp, this algorithm supports pretty printing
- that is only fractionally slower than ordinary printing.
- -------------------------------------------------------------------------------
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 27.2. Pretty Printing Control Variables
-
- [change_begin]
- The function write accepts keyword arguments named :pprint-dispatch,
- :miser-width, :right-margin, and :lines, corresponding to these variables.
-
- [Variable]
- *print-pprint-dispatch*
-
- When *print-pretty* is not nil, printing is controlled by the `pprint dispatch
- table' stored in the variable *print-pprint-dispatch*. The initial value of
- *print-pprint-dispatch* is implementation-dependent and causes traditional
- pretty printing of Lisp code. The last section of this chapter explains how the
- contents of this table can be changed.
-
- [Variable]
- *print-right-margin*
-
- A primary goal of pretty printing is to keep the output between a pair of
- margins. The left margin is set at the column where the output begins. If this
- cannot be determined, the left margin is set to zero.
-
- When *print-right-margin* is not nil, it specifies the right margin to use when
- making layout decisions. When *print-right-margin* is nil (the initial value),
- the right margin is set at the maximum line length that can be displayed by the
- output stream without wraparound or truncation. If this cannot be determined,
- the right margin is set to an implementation-dependent value.
-
- To allow for the possibility of variable-width fonts, *print-right-margin* is
- in units of ems-the width of an ``m'' in the font being used to display
- characters on the relevant output stream at the moment when the variables are
- consulted.
-
- [Variable]
- *print-miser-width*
-
- If *print-miser-width* is not nil, the pretty printer switches to a compact
- style of output (called miser style) whenever the width available for printing
- a substructure is less than or equal to *print-miser-width* ems. The initial
- value of *print-miser-width* is implementation-dependent.
-
- [Variable]
- *print-lines*
-
- When given a value other than its initial value of nil, *print-lines* limits
- the number of output lines produced when something is pretty printed. If an
- attempt is made to go beyond *print-lines* lines, `` ..'' (a space and two
- periods) is printed at the end of the last line followed by all of the suffixes
- (closing delimiters) that are pending to be printed.
-
- (let ((*print-right-margin* 25) (*print-lines* 3))
- (pprint '(progn (setq a 1 b 2 c 3 d 4))))
- (PROGN (SETQ A 1
- B 2
- C 3 ..))
-
- (The symbol ``..'' is printed out to ensure that a reader error will occur if
- the output is later read. A symbol different from ``...'' is used to indicate
- that a different kind of abbreviation has occurred.)
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 27.3. Dynamic Control of the Arrangement of Output
-
- [change_begin]
- The following functions and macros support precise control of what should be
- done when a piece of output is too large to fit in the space available. Three
- concepts underlie the way these operations work: logical blocks, conditional
- newlines, and sections. Before proceeding further, it is important to define
- these terms.
-
- The first line of figure 27-1 shows a schematic piece of output. The characters
- in the output are represented by hyphens. The positions of conditional newlines
- are indicated by digits. The beginnings and ends of logical blocks are
- indicated in the figure by ``<'' and ``>'' respectively.
-
- The output as a whole is a logical block and the outermost section. This
- section is indicated by the 0's on the second line of figure 27-1. Logical
- blocks nested within the output are specified by the macro
- pprint-logical-block. Conditional newline positions are specified by calls on
- pprint-newline. Each conditional newline defines two sections (one before it
- and one after it) and is associated with a third (the section immediately
- containing it).
-
- The section after a conditional newline consists of all the output up to, but
- not including, (a) the next conditional newline immediately contained in the
- same logical block; or if (a) is not applicable, (b) the next newline that is
- at a lesser level of nesting in logical blocks; or if (b) is not applicable,
- (c) the end of the output.
-
- The section before a conditional newline consists of all the output back to,
- but not including, (a) the previous conditional newline that is immediately
- contained in the same logical block; or if (a) is not applicable, (b) the
- beginning of the immediately containing logical block. The last four lines in
- figure 27-1 indicate the sections before and after the four conditional
- newlines.
-
- The section immediately containing a conditional newline is the shortest
- section that contains the conditional newline in question. In figure 27-1, the
- first conditional newline is immediately contained in the section marked with
- 0's, the second and third conditional newlines are immediately contained in the
- section before the fourth conditional newline, and the fourth conditional
- newline is immediately contained in the section after the first conditional
- newline.
-
-
- ----------------------------------------------------------------
- Figure 27-1: Example of Logical Blocks, Conditional Newlines, and Sections
-
- <-1---<--<--2---3->--4-->->
- 000000000000000000000000000
- 11 111111111111111111111111
- 22 222
- 333 3333
- 44444444444444 44444
-
- ----------------------------------------------------------------
-
- Whenever possible, the pretty printer displays the entire contents of a section
- on a single line. However, if the section is too long to fit in the space
- available, line breaks are inserted at conditional newline positions within the
- section.
-
- [Function]
- pprint-newline kind &optional stream
-
- The stream (which defaults to *standard-output*) follows the standard
- conventions for stream arguments to printing functions (that is, nil stands for
- *standard-output* and t stands for *terminal-io*). The kind argument specifies
- the style of conditional newline. It must be one of :linear, :fill, :miser, or
- :mandatory. An error is signaled if any other value is supplied. If stream is a
- pretty printing stream created by pprint-logical-block, a line break is
- inserted in the output when the appropriate condition below is satisfied.
- Otherwise, pprint-newline has no effect. The value nil is always returned.
-
- If kind is :linear, it specifies a `linear-style' conditional newline. A line
- break is inserted if and only if the immediately containing section cannot be
- printed on one line. The effect of this is that line breaks are either inserted
- at every linear-style conditional newline in a logical block or at none of
- them.
-
- If kind is :miser, it specifies a `miser-style' conditional newline. A line
- break is inserted if and only if the immediately containing section cannot be
- printed on one line and miser style is in effect in the immediately containing
- logical block. The effect of this is that miser-style conditional newlines act
- like linear-style conditional newlines, but only when miser style is in effect.
- Miser style is in effect for a logical block if and only if the starting
- position of the logical block is less than or equal to *print-miser-width* from
- the right margin.
-
- If kind is :fill, it specifies a `fill-style' conditional newline. A line break
- is inserted if and only if either (a) the following section cannot be printed
- on the end of the current line, (b) the preceding section was not printed on a
- single line, or (c) the immediately containing section cannot be printed on one
- line and miser style is in effect in the immediately containing logical block.
- If a logical block is broken up into a number of subsections by fill-style
- conditional newlines, the basic effect is that the logical block is printed
- with as many subsections as possible on each line. However, if miser style is
- in effect, fill-style conditional newlines act like linear-style conditional
- newlines.
-
- If kind is :mandatory, it specifies a `mandatory-style' conditional newline. A
- line break is always inserted. This implies that none of the containing
- sections can be printed on a single line and will therefore trigger the
- insertion of line breaks at linear-style conditional newlines in these
- sections.
-
- When a line break is inserted by any type of conditional newline, any blanks
- that immediately precede the conditional newline are omitted from the output
- and indentation is introduced at the beginning of the next line. By default,
- the indentation causes the following line to begin in the same horizontal
- position as the first character in the immediately containing logical block.
- (The indentation can be changed via pprint-indent.)
-
- There are a variety of ways unconditional newlines can be introduced into the
- output (for example, via terpri or by printing a string containing a newline
- character). As with mandatory conditional newlines, this prevents any of the
- containing sections from being printed on one line. In general, when an
- unconditional newline is encountered, it is printed out without suppression of
- the preceding blanks and without any indentation following it. However, if a
- per-line prefix has been specified (see pprint-logical-block), that prefix will
- always be printed no matter how a newline originates.
-
- [Macro]
-
- pprint-logical-block (stream-symbol list
- [[ { :prefix | :per-line-prefix} p | :suffix s ]])
- {form}*
-
- This macro causes printing to be grouped into a logical block. It returns nil.
-
- The stream-symbol must be a symbol. If it is nil, it is treated the same as if
- it were *standard-output*. If it is t, it is treated the same as if it were
- *terminal-io*. The run-time value of stream-symbol must be a stream (or nil
- standing for *standard-output* or t standing for *terminal-io*). The logical
- block is printed into this destination stream.
-
- The body (which consists of the forms) can contain any arbitrary Lisp forms.
- Within the body, stream-symbol (or *standard-output* if stream-symbol is nil,
- or *terminal-io* if stream-symbol is t) is bound to a ``pretty printing''
- stream that supports decisions about the arrangement of output and then
- forwards the output to the destination stream. All the standard printing
- functions (for example, write, princ, terpri) can be used to send output to the
- pretty printing stream created by pprint-logical-block. All and only the output
- sent to this pretty printing stream is treated as being in the logical block.
-
- pprint-logical-block and the pretty printing stream it creates have dynamic
- extent. It is undefined what happens if output is attempted outside of this
- extent to the pretty printing stream created. It is unspecified what happens
- if, within this extent, any output is sent directly to the underlying
- destination stream (by calling write-char, for example).
-
- The :suffix, :prefix, and :per-line-prefix arguments must all be expressions
- that (at run time) evaluate to strings. The :suffix argument s (which defaults
- to the null string) specifies a suffix that is printed just after the logical
- block. The :prefix and :per-line-prefix arguments are mutually exclusive. If
- neither :prefix nor :per-line-prefix is specified, a :prefix of the null string
- is assumed. The :prefix argument specifies a prefix p that is printed before
- the beginning of the logical block. The :per-line-prefix specifies a prefix p
- that is printed before the block and at the beginning of each subsequent line
- in the block. An error is signaled if :prefix and :per-line-prefix are both
- used or if a :suffix, :prefix, or :pre-line-prefix argument does not evaluate
- to a string.
-
- The list is interpreted as being a list that the body is responsible for
- printing. (See pprint-exit-if-list-exhausted and pprint-pop.) If list does not
- (at run time) evaluate to a list, it is printed using write. (This makes it
- easier to write printing functions that are robust in the face of malformed
- arguments.) If *print-circle* (and possibly also *print-shared*) is not nil and
- list is a circular (or shared) reference to a cons, then an appropriate ``#n#''
- marker is printed. (This makes it easy to write printing functions that provide
- full support for circularity and sharing abbreviation.) If *print-level* is not
- nil and the logical block is at a dynamic nesting depth of greater than
- *print-level* in logical blocks, ``#'' is printed. (This makes it easy to write
- printing functions that provide full support for depth abbreviation.)
-
- If any of the three preceding conditions occurs, the indicated output is
- printed on stream-symbol and the body is skipped along with the printing of the
- prefix and suffix. (If the body is not responsible for printing a list, then
- the first two tests above can be turned off by supplying nil for the list
- argument.)
-
- In addition to the list argument of pprint-logical-block, the arguments of the
- standard printing functions such as write, print, pprint, print1, and pprint,
- as well as the arguments of the standard format directives such as ~A, ~S, (and
- ~W) are all checked (when necessary) for circularity and sharing. However, such
- checking is not applied to the arguments of the functions write-line,
- write-string, and write-char or to the literal text output by format. A
- consequence of this is that you must use one of the latter functions if you
- want to print some literal text in the output that is not supposed to be
- checked for circularity or sharing. (See the examples below.)
-
- -------------------------------------------------------------------------------
- Implementation note: Detection of circularity and sharing is supported by the
- pretty printer by in essence performing the requested output twice. On the
- first pass, circularities and sharing are detected and the actual outputting of
- characters is suppressed. On the second pass, the appropriate ``#n='' and
- ``#n#'' markers are inserted and characters are output.
-
- A consequence of this two-pass approach to the detection of circularity and
- sharing is that the body of a pprint-logical-block must not perform any
- side-effects on the surrounding environment. This includes not modifying any
- variables that are bound outside of its scope. Obeying this restriction is
- facilitated by using pprint-pop, instead of an ordinary pop when traversing a
- list being printed by the body of a pprint-logical-block.)
- -------------------------------------------------------------------------------
-
- [Macro]
- pprint-exit-if-list-exhausted
-
- pprint-exit-if-list-exhausted tests whether or not the list argument of
- pprint-logical-block has been exhausted (see pprint-pop). If this list has been
- reduced to nil, pprint-exit-if-list-exhausted terminates the execution of the
- immediately containing pprint-logical-block except for the printing of the
- suffix. Otherwise pprint-exit-if-list-exhausted returns nil. An error message
- is issued if pprint-exit-if-list-exhausted is used anywhere other than
- syntactically nested within a call on pprint-logical-block. It is undefined
- what happens if pprint-pop is executed outside of the dynamic extent of this
- pprint-logical-block.
-
- [Macro]
- pprint-pop
-
- pprint-pop pops elements one at a time off the list argument of
- pprint-logical-block, taking care to obey *print-length*, *print-circle*, and
- *print-shared*. An error message is issued if it is used anywhere other than
- syntactically nested within a call on pprint-logical-block. It is undefined
- what happens if pprint-pop is executed outside of the dynamic extent of this
- call on pprint-logical-block.
-
- Each time pprint-pop is called, it pops the next value off the list argument of
- pprint-logical-block and returns it. However, before doing this, it performs
- three tests. If the remaining list is not a list (neither a cons nor nil), ``.
- '' is printed followed by the remaining list. (This makes it easier to write
- printing functions that are robust in the face of malformed arguments.) If
- *print-length* is nil and pprint-pop has already been called *print-length*
- times within the immediately containing logical block, ``...'' is printed.
- (This makes it easy to write printing functions that properly handle
- *print-length*.) If *print-circle* (and possibly also *print-shared*) is not
- nil, and the remaining list is a circular (or shared) reference, then ``. '' is
- printed followed by an appropriate ``#n#'' marker. (This catches instances of
- cdr circularity and sharing in lists.)
-
- If any of the three preceding conditions occurs, the indicated output is
- printed on the pretty printing stream created by the immediately containing
- pprint-logical-block and the execution of the immediately containing
- pprint-logical-block is terminated except for the printing of the suffix.
-
- If pprint-logical-block is given a list argument of nil-because it is not
- processing a list-pprint-pop can still be used to obtain support for
- *print-length* (see the example function pprint-vector below). In this
- situation, the first and third tests above are disabled and pprint-pop always
- returns nil.
-
- [Function]
- pprint-indent relative-to n &optional stream
-
- pprint-indent specifies the indentation to use in a logical block. Stream
- (which defaults to *standard-output*) follows the standard conventions for
- stream arguments to printing functions. The argument n specifies the
- indentation in ems. If relative-to is :block, the indentation is set to the
- horizontal position of the first character in the block plus n ems. If
- relative-to is :current, the indentation is set to the current output position
- plus n ems.
-
- The argument n can be negative; however, the total indentation cannot be moved
- left of the beginning of the line or left of the end of the rightmost per-line
- prefix. Changes in indentation caused by pprint-indent do not take effect until
- after the next line break. In addition, in miser mode all calls on
- pprint-indent are ignored, forcing the lines corresponding to the logical block
- to line up under the first character in the block.
-
- An error is signaled if a value other than :block or :current is supplied for
- relative-to. If stream is a pretty printing stream created by
- pprint-logical-block, pprint-indent sets the indentation in the innermost
- dynamically enclosing logical block. Otherwise, pprint-indent has no effect.
- The value nil is always returned.
-
- [Function]
- pprint-tab kind colnum colinc &optional stream
-
- pprint-tab specifies tabbing as performed by the standard format directive ~T.
- Stream (which defaults to *standard-output*) follows the standard conventions
- for stream arguments to printing functions. The arguments colnum and colinc
- correspond to the two parameters to ~T and are in terms of ems. The kind
- argument specifies the style of tabbing. It must be one of :line (tab as by ~T)
- :section (tab as by ~T, but measuring horizontal positions relative to the
- start of the dynamically enclosing section), :line-relative (tab as by ~@T), or
- :section-relative (tab as by ~@T, but measuring horizontal positions relative
- to the start of the dynamically enclosing section). An error is signaled if any
- other value is supplied for kind. If stream is a pretty printing stream created
- by pprint-logical-block, tabbing is performed. Otherwise, pprint-tab has no
- effect. The value nil is always returned.
-
- [Function]
- pprint-fill stream list &optional colon? atsign?
- pprint-linear stream list &optional colon? atsign?
- pprint-tabular stream list &optional colon? atsign? tabsize
-
- These three functions specify particular ways of pretty printing lists. Stream
- follows the standard conventions for stream arguments to printing functions.
- Each function prints parentheses around the output if and only if colon?
- (default t) is not nil. Each function ignores its atsign? argument and returns
- nil. (These two arguments are included in this way so that these functions can
- be used via ~/.../ and as set-pprint-dispatch functions as well as directly.)
- Each function handles abbreviation and the detection of circularity and sharing
- correctly and uses write to print list when given a non-list argument.
-
- The function pprint-linear prints a list either all on one line or with each
- element on a separate line. The function pprint-fill prints a list with as many
- elements as possible on each line. The function pprint-tabular is the same as
- pprint-fill except that it prints the elements so that they line up in columns.
- This function takes an additional argument tabsize (default 16) that specifies
- the column spacing in ems.
-
- As an example of the interaction of logical blocks, conditional newlines, and
- indentation, consider the function pprint-defun below. This function pretty
- prints a list whose car is defun in the standard way assuming that the length
- of the list is exactly 4.
-
- ;;; Pretty printer function for DEFUN forms.
-
- (defun pprint-defun (list)
- (pprint-logical-block (nil list :prefix "(" :suffix ")")
- (write (first list))
- (write-char #\space)
- (pprint-newline :miser)
- (pprint-indent :current 0)
- (write (second list))
- (write-char #\space)
- (pprint-newline :fill)
- (write (third list))
- (pprint-indent :block 1)
- (write-char #\space)
- (pprint-newline :linear)
- (write (fourth list))))
-
- Suppose that one evaluates the following:
-
- (pprint-defun '(defun prod (x y) (* x y)))
-
- If the line width available is greater than or equal to 26, all of the output
- appears on one line. If the width is reduced to 25, a line break is inserted at
- the linear-style conditional newline before (* X Y), producing the output shown
- below. The (pprint-indent :block 1) causes (* X Y) to be printed at a relative
- indentation of 1 in the logical block.
-
- (DEFUN PROD (X Y)
- (* X Y))
-
- If the width is 15, a line break is also inserted at the fill-style conditional
- newline before the argument list. The argument list lines up under the function
- name because of the call on (pprint-indent :current 0) before the printing of
- the function name.
-
- (DEFUN PROD
- (X Y)
- (* X Y))
-
- If *print-miser-width* were greater than or equal to 14, the output would have
- been entirely in miser mode. All indentation changes are ignored in miser mode
- and line breaks are inserted at miser-style conditional newlines. The result
- would have been as follows:
-
- (DEFUN
- PROD
- (X Y)
- (* X Y))
-
- As an example of the use of a per-line prefix, consider that evaluating the
- expression
-
- (pprint-logical-block (nil nil :per-line-prefix ";;; ")
- (pprint-defun '(defun prod (x y) (* x y))))
-
- produces the output
-
- ;;; (DEFUN PROD
- ;;; (X Y)
- ;;; (* X Y))
-
- with a line width of 20 and nil as the value of the printer control variable
- *print-miser-width*.
-
- (If *print-miser-width* were not nil the output
-
- ;;; (DEFUN
- ;;; PROD
- ;;; (X Y)
- ;;; (* X Y))
-
- might appear instead.)
-
- As a more complex (and realistic) example, consider the function pprint-let
- below. This specifies how to pretty print a let in the standard style. It is
- more complex than pprint-defun because it has to deal with nested structure.
- Also, unlike pprint-defun, it contains complete code to print readably any
- possible list that begins with the symbol let. The outermost
- pprint-logical-block handles the printing of the input list as a whole and
- specifies that parentheses should be printed in the output. The second
- pprint-logical-block handles the list of binding pairs. Each pair in the list
- is itself printed by the innermost pprint-logical-block. (A loop is used
- instead of merely decomposing the pair into two elements so that readable
- output will be produced no matter whether the list corresponding to the pair
- has one element, two elements, or (being malformed) has more than two
- elements.) A space and a fill-style conditional newline are placed after each
- pair except the last. The loop at the end of the topmost pprint-logical-block
- prints out the forms in the body of the let separated by spaces and
- linear-style conditional newlines.
-
- ;;; Pretty printer function for LET forms,
- ;;; carefully coded to handle malformed binding pairs.
-
- (defun pprint-let (list)
- (pprint-logical-block (nil list :prefix "(" :suffix ")")
- (write (pprint-pop))
- (pprint-exit-if-list-exhausted)
- (write-char #\space)
- (pprint-logical-block
- (nil (pprint-pop) :prefix "(" :suffix ")")
- (pprint-exit-if-list-exhausted)
- (loop (pprint-logical-block
- (nil (pprint-pop) :prefix "(" :suffix ")")
- (pprint-exit-if-list-exhausted)
- (loop (write (pprint-pop))
- (pprint-exit-if-list-exhausted)
- (write-char #\space)
- (pprint-newline :linear)))
- (pprint-exit-if-list-exhausted)
- (write-char #\space)
- (pprint-newline :fill)))
- (pprint-indent :block 1)
- (loop (pprint-exit-if-list-exhausted)
- (write-char #\space)
- (pprint-newline :linear)
- (write (pprint-pop)))))
-
- Suppose that the following is evaluated with *print-level* having the value 4
- and *print-circle* having the value t.
-
- (pprint-let '#1=(let (x (*print-length* (f (g 3)))
- (z . 2) (k (car y)))
- (setq x (sqrt z)) #1#))
-
- If the line length is greater than or equal to 77, the output produced appears
- on one line. However, if the line length is 76, line breaks are inserted at the
- linear-style conditional newlines separating the forms in the body and the
- output below is produced. Note that the degenerate binding pair X is printed
- readably even though it fails to be a list; a depth abbreviation marker is
- printed in place of (G 3); the binding pair (Z . 2) is printed readably even
- though it is not a proper list; and appropriate circularity markers are
- printed.
-
- #1=(LET (X (*PRINT-LENGTH* (F #)) (Z . 2) (K (CAR Y)))
- (SETQ X (SQRT Z))
- #1#)
-
- If the line length is reduced to 35, a line break is inserted at one of the
- fill-style conditional newlines separating the binding pairs.
-
- #1=(LET (X (*PRINT-PRETTY* (F #))
- (Z . 2) (K (CAR Y)))
- (SETQ X (SQRT Z))
- #1#)
-
- Suppose that the line length is further reduced to 22 and *print-length* is set
- to 3. In this situation, line breaks are inserted after both the first and
- second binding pairs. In addition, the second binding pair is itself broken
- across two lines. Clause (b) of the description of fill-style conditional
- newlines prevents the binding pair (Z . 2) from being printed at the end of the
- third line. Note that the length abbreviation hides the circularity from view
- and therefore the printing of circularity markers disappears.
-
- (LET (X
- (*PRINT-LENGTH*
- (F #))
- (Z . 2) ...)
- (SETQ X (SQRT Z))
- ...)
-
- The function pprint-tabular could be defined as follows:
-
- (defun pprint-tabular (s list &optional (c? t) a? (size 16))
- (declare (ignore a?))
- (pprint-logical-block
- (s list :prefix (if c? "(" "") :suffix (if c? ")" ""))
- (pprint-exit-if-list-exhausted)
- (loop (write (pprint-pop) :stream s)
- (pprint-exit-if-list-exhausted)
- (write-char #\space s)
- (pprint-tab :section-relative 0 size s)
- (pprint-newline :fill s))))
-
- Evaluating the following with a line length of 25 produces the output shown.
-
- (princ "Roads ")
- (pprint-tabular nil '(elm main maple center) nil nil 8)
- Roads ELM MAIN
- MAPLE CENTER
-
- The function below prints a vector using #(...) notation.
-
- (defun pprint-vector (v)
- (pprint-logical-block (nil nil :prefix "#(" :suffix ")")
- (let ((end (length v)) (i 0))
- (when (plusp end)
- (loop (pprint-pop)
- (write (aref v i))
- (if (= (incf i) end) (return nil))
- (write-char #\space)
- (pprint-newline :fill))))))
-
- Evaluating the following with a line length of 15 produces the output shown.
-
- (pprint-vector '#(12 34 567 8 9012 34 567 89 0 1 23))
-
- #(12 34 567 8
- 9012 34 567
- 89 0 1 23)
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 27.4. Format Directive Interface
-
- [change_begin]
- The primary interface to operations for dynamically determining the arrangement
- of output is provided through the functions above. However, an additional
- interface is provided via a set of format directives because, as shown by the
- examples in this section and the next, format strings are typically a much more
- compact way to specify pretty printing. In addition, without such an interface,
- one would have to abandon the use of format when interacting with the pretty
- printer.
-
- ~W Write. An arg, any Lisp object, is printed obeying every printer control
- variable (as by write). In addition, ~W interacts correctly with depth
- abbreviation by not resetting the depth counter to zero. ~W does not
- accept parameters. If given the colon modifier, ~W binds *print-pretty* to
- t. If given the atsign modifier, ~W binds *print-level* and *print-length*
- to nil.
-
- ~W provides automatic support for circularity detection. If *print-circle*
- (and possibly also *print-shared*) is not nil and ~W is applied to an
- argument that is a circular (or shared) reference, an appropriate ``#n#''
- marker is inserted in the output instead of printing the argument.
-
- ~_ Conditional newline. Without any modifiers, ~_ is equivalent to
- (pprint-newline :linear). The directive ~@_ is equivalent to
- (pprint-newline :miser). The directive ~:_ is equivalent to
- (pprint-newline :fill). The directive ~:@_ is equivalent to
- (pprint-newline :mandatory).
-
- ~<str~:>
- Logical block. If ~:> is used to terminate a ~<... directive, the
- directive is equivalent to a call on pprint-logical-block. The format
- argument corresponding to the ~<...~:> directive is treated in the same
- way as the list argument to pprint-logical-block, thereby providing
- automatic support for non-list arguments and the detection of circularity,
- sharing, and depth abbreviation. The portion of the format control string
- nested within the ~<...~:> specifies the :prefix (or :per-line-prefix),
- :suffix, and body of the pprint-logical-block.
-
- The format string portion enclosed by ~<...~:> can be divided into
- segments ~<prefix~;body~;suffix~:> by ~; directives. If the first section
- is terminated by ~@;, it specifies a per-line prefix rather than a simple
- prefix. The prefix and suffix cannot contain format directives. An error
- is signaled if either the prefix or suffix fails to be a constant string
- or if the enclosed portion is divided into more than three segments.
-
- If the enclosed portion is divided into only two segments, the suffix
- defaults to the null string. If the enclosed portion consists of only a
- single segment, both the prefix and the suffix default to the null string.
- If the colon modifier is used (that is, ~:<...~:>), the prefix and suffix
- default to "(" and ")", respectively, instead of the null string.
-
- The body segment can be any arbitrary format control string. This format
- control string is applied to the elements of the list corresponding to the
- ~<...~:> directive as a whole. Elements are extracted from this list using
- pprint-pop, thereby providing automatic support for malformed lists and
- the detection of circularity, sharing, and length abbreviation. Within the
- body segment, ~^ acts like pprint-exit-if-list-exhausted.
-
- ~<...~:> supports a feature not supported by pprint-logical-block. If ~:@>
- is used to terminate the directive (that is, ~<...~:@>), then a fill-style
- conditional newline is automatically inserted after each group of blanks
- immediately contained in the body (except for blanks after a ~<newline>
- directive). This makes it easy to achieve the equivalent of paragraph
- filling.
-
- If the atsign modifier is used with ~<...~:>, the entire remaining
- argument list is passed to the directive as its argument. All of the
- remaining arguments are always consumed by ~@<...~:>, even if they are not
- all used by the format string nested in the directive. Other than the
- difference in its argument, ~@<...~:> is exactly the same as ~<...~:>,
- except that circularity (and sharing) detection is not applied if the
- ~@<...~:> is at top level in a format string. This ensures that
- circularity detection is applied only to data lists and not to format
- argument lists.
-
- To a considerable extent, the basic form of the directive ~<...~> is
- incompatible with the dynamic control of the arrangement of output by ~W,
- ~_, ~<...~:>, ~I, and ~:T. As a result, an error is signaled if any of
- these directives is nested within ~<...~>. Beyond this, an error is also
- signaled if the ~<...~:;...~> form of ~<...~> is used in the same format
- string with ~W, ~_, ~<...~:>, ~I, or ~:T.
-
- ~I Indent. ~nI is equivalent to (pprint-indent :block n). ~:nI is equivalent
- to (pprint-indent :current n). In both cases, n defaults to zero if it is
- omitted.
-
- ~:T Tabulate. If the colon modifier is used with the ~T directive, the tabbing
- computation is done relative to the column where the section immediately
- containing the directive begins, rather than with respect to column zero.
- ~n,m:T is equivalent to (pprint-tab :section n m). ~n,m:@T is equivalent
- to (pprint-tab :section-relative n m). The numerical parameters are both
- interpreted as being in units of ems and both default to 1.
-
- ~/name/
- Call function. User-defined functions can be called from within a format
- string by using the directive ~/name/. The colon modifier, the atsign
- modifier, and arbitrarily many parameters can be specified with the
- ~/name/ directive. The name can be any string that does not contain ``/''.
- All of the characters in name are treated as if they were upper case. If
- name contains a ``:'' or ``::'', then everything up to but not including
- the first ``:'' or ``::'' is taken to be a string that names a package.
- Everything after the first ``:'' or ``::'' (if any) is taken to be a
- string that names a symbol. The function corresponding to a ~/name/
- directive is obtained by looking up the symbol that has the indicated name
- in the indicated package. If name does not contain a ``:'' or ``::'', then
- the whole name string is looked up in the user package.
-
- When a ~/name/ directive is encountered, the indicated function is called
- with four or more arguments. The first four arguments are the output
- stream, the format argument corresponding to the directive, the value t if
- the colon modifier was used (nil otherwise), and the value t if the atsign
- modifier was used (nil otherwise). The remaining arguments consist of any
- parameters specified with the directive. The function should print the
- argument appropriately. Any values returned by the function are ignored.
-
- The three functions pprint-linear, pprint-fill, and pprint-tabular are
- designed so that they can be called by ~/.../ (that is, ~/pprint-linear/,
- ~/pprint-fill/, and ~/pprint-tabular/. In particular they take colon and
- atsign arguments.
-
- As examples of the convenience of specifying pretty printing with format
- strings, consider the functions pprint-defun and pprint-let used as examples in
- the last section. They can be more compactly defined as follows. The function
- pprint-vector cannot be defined using format, because the data structure it
- traverses is not a list. The function pprint-tabular is inconvenient to define
- using format, because of the need to pass its tabsize argument through to a ~:T
- directive nested within an iteration over a list.
-
- (defun pprint-defun (list)
- (format t
- "~:<~W ~@_~:I~W ~:_~W~1I ~_~W~:>"
- list))
-
- (defun pprint-let (list)
- (format t "~:<~W~^ ~:<~@{~:<~@{~W~^ ~_~}~:>~^ ~:_~}~:>~1I~
- ~@{~^ ~_~W~}~:>"
- list))
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 27.5. Compiling Format Control Strings
-
- [change_begin]
- The control strings used by format are essentially programs that perform
- printing. The macro formatter provides the efficiency of using a compiled
- function for printing without losing the visual compactness of format strings.
-
- [Macro]
- formatter control-string
-
- The control-string must be a literal string. An error is signaled if
- control-string is not a valid format control string. The macro formatter
- expands into an expression of the form (function (lambda (stream &rest args)
- ...)) that does the printing specified by control-string. The lambda created
- accepts an output stream as its first argument and zero or more data values as
- its remaining arguments. The value returned by the lambda is the tail (if any)
- of the data values that are not printed out by control-string. (For example, if
- the control-string is "~A~A", the cddr (if any) of the data values is
- returned.) The form (formatter "~%~2@{~S, ~}") is equivalent to the following:
-
- #'(lambda (stream &rest args)
- (terpri stream)
- (dotimes (n 2)
- (if (null args) (return nil))
- (prin1 (pop args) stream)
- (write-string ", " stream))
- args)
-
- In support of the above mechanism, format is extended so that it accepts
- functions as its second argument as well as strings. When a function is
- provided, it must be a function of the form created by formatter. The function
- is called with the appropriate output stream as its first argument and the data
- arguments to format as its remaining arguments. The function should perform
- whatever output is necessary and return the unused tail of the arguments (if
- any). The directives and ~{~} with no body are also extended so that they
- accept functions as well as control strings. Every other standard function that
- takes a format string as an argument (for example, error and warn) is also
- extended so that it can accept functions of the form above instead.
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 27.6. Pretty Printing Dispatch Tables
-
- [change_begin]
- When *print-pretty* is not nil, the pprint dispatch table in the variable
- *print-pprint-dispatch* controls how objects are printed. The information in
- this table takes precedence over all other mechanisms for specifying how to
- print objects. In particular, it overrides user-defined print-object methods
- and print functions for structures. However, if there is no specification for
- how to pretty print a particular kind of object, it is then printed using the
- standard mechanisms as if *print-pretty* were nil.
-
- A pprint dispatch table is a mapping from keys to pairs of values. The keys are
- type specifiers. The values are functions and numerical priorities. Basic
- insertion and retrieval is done based on the keys with the equality of keys
- being tested by equal. The function to use when pretty printing an object is
- chosen by finding the highest priority function in *print-pprint-dispatch* that
- is associated with a type specifier that matches the object.
-
- [Function]
- copy-pprint-dispatch &optional table
-
- A copy is made of table, which defaults to the current pprint dispatch table.
- If table is nil, a copy is returned of the initial value of
- *print-pprint-dispatch*.
-
- [Function]
- pprint-dispatch object &optional table
-
- This retrieves the highest priority function from a pprint table that is
- associated with a type specifier in the table that matches object. The function
- is chosen by finding all the type specifiers in table that match the object and
- selecting the highest priority function associated with any of these type
- specifiers. If there is more than one highest priority function, an arbitrary
- choice is made. If no type specifiers match the object, a function is returned
- that prints object with *print-pretty* bound to nil.
-
- As a second return value, pprint-dispatch returns a flag that is t if a
- matching type specifier was found in table and nil if not.
-
- Table (which defaults to *print-pprint-dispatch*) must be a pprint dispatch
- table. Table can be nil, in which case retrieval is done in the initial value
- of *print-pprint-dispatch*.
-
- When *print-pretty* is t, (write object :stream s) is equivalent to (funcall
- (pprint-dispatch object) s object).
-
- [Function]
- set-pprint-dispatch type function &optional priority table
-
- This puts an entry into a pprint dispatch table and returns nil. The type must
- be a valid type specifier and is the key of the entry. The first action of
- set-pprint-dispatch is to remove any pre-existing entry associated with type.
- This guarantees that there will never be two entries associated with the same
- type specifier in a given pprint dispatch table. Equality of type specifiers is
- tested by equal.
-
- Two values are associated with each type specifier in a pprint dispatch table:
- a function and a priority. The function must accept two arguments: the stream
- to send output to and the object to be printed. The function should pretty
- print the object on the stream. The function can assume that object satisfies
- type. The function should obey *print-readably*. Any values returned by the
- function are ignored.
-
- The priority (which defaults to 0) must be a non-complex number. This number is
- used as a priority to resolve conflicts when an object matches more than one
- entry. An error is signaled if priority fails to be a non-complex number.
-
- The table (which defaults to the value of *print-pprint-dispatch*) must be a
- pprint dispatch table. The specified entry is placed in this table.
-
- It is permissible for function to be nil. In this situation, there will be no
- type entry in table after set-pprint-dispatch is evaluated.
-
- To facilitate the use of pprint dispatch tables for controlling the pretty
- printing of Lisp code, the type-specifier argument of the function
- set-pprint-dispatch is allowed to contain the form (cons car-type cdr-type).
- This form indicates that the corresponding object must be a cons whose car
- satisfies the type specifier car-type and whose cdr satisfies the type
- specifier cdr-type. The cdr-type can be omitted, in which case it defaults to
- t.
-
- The initial value of *print-pprint-dispatch* is implementation-dependent.
- However, the initial entries all use a special class of priorities that are
- less than every priority that can be specified using set-pprint-dispatch. This
- guarantees that pretty printing functions specified by users will override
- everything in the initial value of *print-pprint-dispatch*.
-
- Consider the following examples. The first form restores
- *print-pprint-dispatch* to its initial value. The next two forms then specify a
- special way of pretty printing ratios. Note that the more specific type
- specifier has to be associated with a higher priority.
-
- (setq *print-pprint-dispatch*
- (copy-pprint-dispatch nil))
-
- (defun div-print (s r colon? atsign?)
- (declare (ignore colon? atsign?))
- (format s "(/ ~D ~D)" (numerator (abs r)) (denominator r)))
-
- (set-pprint-dispatch 'ratio (formatter "#.~/div-print/"))
-
- (set-pprint-dispatch '(and ratio (satisfies minusp))
- (formatter "#.(- ~/div-print/)")
- 5)
-
- (pprint '(1/3 -2/3)) prints: (#.(/ 1 3) #.(- (/ 2 3)))
-
- The following two forms illustrate the specification of pretty printing
- functions for particular types of Lisp code. The first form illustrates how to
- specify the traditional method for printing quoted objects using ``''' syntax.
- Note the care taken to ensure that data lists that happen to begin with quote
- will be printed readably. The second form specifies that lists beginning with
- the symbol my-let should print the same way that lists beginning with let print
- when the initial pprint dispatch table is in effect.
-
- (set-pprint-dispatch '(cons (member quote))
- #'(lambda (s list)
- (if (and (consp (cdr list)) (null (cddr list)))
- (funcall (formatter "'~W") s (cadr list))
- (pprint-fill s list)))))
-
- (set-pprint-dispatch '(cons (member my-let))
- (pprint-dispatch '(let) nil))
-
- The next example specifies a default method for printing lists that do not
- correspond to function calls. Note that, as shown in the definition of
- pprint-tabular above, pprint-linear, pprint-fill, and pprint-tabular are
- defined with optional colon and atsign arguments so that they can be used as
- pprint dispatch functions as well as ~/.../ functions.
-
- (set-pprint-dispatch
- '(cons (not (and symbol (satisfies fboundp))))
- #'pprint-fill
- -5)
-
- With a line length of 9, (pprint '(0 b c d e f g h i j k)) prints:
-
- (0 b c d
- e f g h
- i j k)
-
- This final example shows how to define a pretty printing function for a user
- defined data structure.
-
- (defstruct family mom kids)
-
- (set-pprint-dispatch 'family
- #'(lambda (s f)
- (format s "~@<#<~;~W and ~2I~_~/pprint-fill/~;>~:>"
- (family-mom f) (family-kids f))))
-
- The pretty printing function for the structure family specifies how to adjust
- the layout of the output so that it can fit aesthetically into a variety of
- line widths. In addition, it obeys the printer control variables *print-level*,
- *print-length*, *print-lines*, *print-circle*, *print-shared*, and
- *print-escape*, and can tolerate several different kinds of malformity in the
- data structure. The output below shows what is printed out with a right margin
- of 25, *print-pretty* t, *print-escape* nil, and a malformed kids list.
-
- (write (list 'principal-family
- (make-family :mom "Lucy"
- :kids '("Mark" "Bob" . "Dan")))
- :right-margin 25 :pretty T :escape nil :miser-width nil)
-
- (PRINCIPAL-FAMILY
- #<Lucy and
- Mark Bob . Dan>)
-
- Note that a pretty printing function for a structure is different from the
- structure's print function. While print functions are permanently associated
- with a structure, pretty printing functions are stored in pprint dispatch
- tables and can be rapidly changed to reflect different printing needs. If there
- is no pretty printing function for a structure in the current print dispatch
- table, the print function (if any) is used instead.
-
- [change_end]
-
- -------------------------------------------------------------------------------
-
-
-
-
-
-